import React, { Component } from 'react'
import './index.css'

/**
 * json编辑器
 * @author liu_jshan
 * @date 2024-10-16
 */
export default class JsonEditor extends Component {

    constructor(props) {
        super(props)
        this.state = {
            inputValue: '',
            jsonFormartValue: '',
            jsonFormartHtml: '',
            jsonNoHtml: '' //序号
        }
    }

    async componentDidMount() {
        if (this.props.value) {
            await this.onChange(this.props.value)
        }
    }

    /**
     * 输入框变化时触发的操作
     * @param {string} value 变化后的值
     * @param {Function} callback 回调函数
     */
    onChange = async (value, callBack) => {
        let jsonFormartHtml = ''
        let jsonNoHtml = ''
        try {
            //首先将输入框的内容解析为json
            const formartData = this.jsonFomart(JSON.parse(value), true)
            jsonFormartHtml = formartData.jsonFormartHtml
            jsonNoHtml = formartData.jsonNoHtml
        } catch (err) {
            console.log(err.message);
            const matchRes = err.message.match(/line \d+/g)
            let errorLine = -1
            if (Array.isArray(matchRes) && matchRes.length > 0) {
                errorLine = Number(matchRes[0].replace('line ', '')) //错误行数
            }
            const formartData = this.jsonFomart(value + '\r', false, errorLine)
            jsonFormartHtml = formartData.jsonFormartHtml
            jsonNoHtml = formartData.jsonNoHtml
        }
        this.setState({
            inputValue: value,
            jsonFormartHtml,
            jsonNoHtml
        }, callBack)

        typeof this.props.onChange === 'function' && await this.props.onChange(value)
    }

    onKeyDown = (event) => {
        if (event.key === 'Tab') {
            // 阻止默认的 Tab 行为（切换焦点）  
            event.preventDefault();
            // 在输入框中插入空格
            const selectionStart = event.target.selectionStart;
            const selectionEnd = event.target.selectionEnd;
            // 插入空格并更新光标位置  
            const newValue = this.state.inputValue.slice(0, selectionStart) + '  ' + this.state.inputValue.slice(selectionEnd)
            this.onChange(newValue, () => {
                this.inputRef.setSelectionRange(selectionStart + 2, selectionStart + 2)
            })
        }
    }

    /**
     * json格式化
     * @param {string} json json数据 
     * @param {boolean} isTrue 解析是否正茶馆
     * @param {Number} errorLine 错误行数
     * @returns 格式化后的字符串形式html
     */
    jsonFomart = (json, isTrue, errorLine) => {
        function replacer(key, value) {
            return value
        }

        if (isTrue) {
            json = JSON.stringify(json, replacer, 2);
        }
        json = json.replace(/("(?:[^"\\]|\\.)*")(?=\s*:)/g, '<span class="green">$1</span>')
        json = json.replace(/:\s*("(?:[^"\\]|\\.)*")/g, ': <span class="green">$1</span>')
        json = json.replace(/:\s*(true|false)/g, ': <span class="purple">$1</span>')
        json = json.replace(/:\s*(null|\d+(\.\d+)?)/g, ': <span class="blue">$1</span>')
        //添加行号
        const jsonArray = json.split(/\n/g)
        let jsonNoHtml = ''
        if (isTrue) {
            for (let i = 0; i < jsonArray.length; i++) {
                jsonNoHtml += `[${i + 1}]\n`
            }
        } else {
            for (let i = 0; i < jsonArray.length; i++) {
                if (errorLine > 0) {
                    jsonNoHtml += errorLine === i + 1 ? `❌[${i + 1}]\n` : `[${i + 1}]\n`
                } else {
                    jsonNoHtml += `❌[${i + 1}]\n`
                }
            }
        }

        return {
            jsonNoHtml,
            jsonFormartHtml: json
        }
    }

    render() {
        return (
            <div className='editor-body'>
                <div className="editor-container">
                    <div className="editor-input-area">
                        <textarea
                            className='editor-textarea'
                            value={this.state.inputValue}
                            placeholder='在这里输入JSON数据...'
                            onChange={event => { this.onChange(event.target.value) }}
                            onKeyDown={this.onKeyDown}
                            ref={ref => this.inputRef = ref}
                        ></textarea>
                    </div>
                    <div className="editor-output-area">
                        <div className='editor-output-no' dangerouslySetInnerHTML={{ __html: this.state.jsonNoHtml }}></div>
                        <div 
                            className='editor-output-content' 
                            dangerouslySetInnerHTML={{ __html: this.state.jsonFormartHtml }}
                        ></div>
                    </div>
                </div>
            </div>
        )
    }
}
